#include "Work.h"
#include "Logger.h"
#include "Common/Common.h"
#include "Common/NetworkProcessor.h"
#include "DBManager.h"

using namespace System;
using namespace System::IO;
using namespace System::Net;
using namespace System::Net::Sockets;

using namespace SPlayer::Common;
using namespace SPlayer::Network;

using namespace SPlayerServer;

void SPlayerServer::initWork()
{
    try
    {
        int port = Network::NetworkInfo::port;
        System::Net::Sockets::TcpListener ^listener =
            System::Net::Sockets::TcpListener::Create(port);
        listener->AllowNatTraversal(true);
        listener->Start();
        Logger::log(LogLevel::Information, "Start to listen on port {0}", port);

        auto func = gcnew AsyncCallback(&SPlayerServer::onAccept);
        listener->BeginAcceptSocket(func, listener);
        delete func;
    }
    catch (System::Exception ^e)
    {
        Logger::log(LogLevel::Error, "Failed to startup Server : {0}", e);
    }
}
void onDownloadMusic(NetworkProcessor ^processor)
{
    auto params = processor->receivedParams;
    auto title = (String ^)params["Title"];
    auto offset = (int)params["Offset"];
    auto size = (int)params["Size"];

    Logger::log(LogLevel::Debug, "onDownloadMusic");
    Logger::log(LogLevel::Debug, "title : {0}", title);
    Logger::log(LogLevel::Debug, "offset : {0}", offset);
    Logger::log(LogLevel::Debug, "size : {0}", size);

    auto record = DBManager::getMusic(title);
    if (!record)
    {
        Logger::log(LogLevel::Warning, "Error in onDownloadMusic : Music {0} Not Found!", title);

        processor->sendParams->add("Response", "Error");
        processor->sendParams->add("Message", "Music not found in database");
        processor->send();
        processor->close(2000);
        return;
    }

    FileInfo fi(record->filepath);

    processor->sendParams->add(processor->receivedParams);
    processor->sendParams->add("FileSize", static_cast<int>(fi.Length));

    FileStream fs(fi.FullName, System::IO::FileMode::Open, System::IO::FileAccess::Read, System::IO::FileShare::Read);
    auto data = gcnew array<unsigned char>(static_cast<int>(fi.Length));
    fs.Read(data, 0, data->Length);

    processor->sendData = data;
    processor->send();
    Logger::log(LogLevel::Debug, "Sent Music : Length : {0}", data->Length);
    return;
}

void onDownloadLyric(NetworkProcessor ^processor)
{
    auto params = processor->receivedParams;
    auto title = (String ^)params["Title"];

    Logger::log(LogLevel::Debug, "onDownloadLyric");
    Logger::log(LogLevel::Debug, "title : {0}", title);

    auto record = DBManager::getMusic(title);
    if (!record)
    {
        Logger::log(LogLevel::Warning, "Error in onDownloadLyric : Music {0} Not Found!", title);

        processor->sendParams->add("Response", "Error");
        processor->sendParams->add("Message", "Music not found in database");
        processor->send();
        processor->close(2000);
        return;
    }
    if (!record->hasLyric)
    {
        Logger::log(LogLevel::Debug, "No Lyric for {0}", title);

        processor->sendParams->add("Response", "Error");
        processor->sendParams->add("Message", "No Lyric for this music");
        processor->send();
        processor->close(2000);
        return;
    }

    FileInfo fi(record->lyricPath);

    processor->sendParams->add(processor->receivedParams);
    processor->sendParams->add("FileSize", static_cast<int>(fi.Length));

    FileStream fs(fi.FullName, System::IO::FileMode::Open, System::IO::FileAccess::Read, System::IO::FileShare::Read);
    auto data = gcnew array<unsigned char>(static_cast<int>(fi.Length));
    fs.Read(data, 0, data->Length);

    processor->sendData = data;
    processor->send();
    Logger::log(LogLevel::Debug, "Sent Lyric : Length : {0}", data->Length);
    return;
}

void onSearchMusic(NetworkProcessor ^processor)
{
    auto params = processor->receivedParams;
    auto keywordString = (String ^)params["KeywordString"];

    Logger::log(LogLevel::Debug, "onSearchMusic");
    Logger::log(LogLevel::Debug, "KeyWords : {0}", keywordString);
    if (keywordString->Length == 0)
    {
        processor->sendParams->add("Response", "Error");
        processor->sendParams->add("Message", "Search keyword too short!");
        processor->send();
        processor->close();
        return;
    }

    auto result = DBManager::query(keywordString);

    processor->sendParams->add(processor->receivedParams);
    processor->sendParams->add("Number", result->Count);

    MemoryStream ^memstream = gcnew MemoryStream;
    BinaryWriter ^writer = gcnew BinaryWriter(memstream);
    for each (Record ^r in result)
    {
        auto container = gcnew NetworkParamCollection;
        container->add("Title", r->title);
        container->add("PlayLength", r->playLength);
        container->add("HasLyric", r->hasLyric);

        auto arr = Text::Encoding::UTF8->GetBytes(container->toString());
        writer->Write(static_cast<int>(arr->Length));
        writer->Write(arr);        
    }
    writer->Flush();
    processor->sendData = memstream->ToArray();  
    processor->send();
    delete memstream;
    delete writer;
    Logger::log(LogLevel::Debug, "Sent Search Result : Count : {0}, Length : {1}",
                result->Count, processor->sendData->Length);
    return;
}

void SPlayerServer::onAccept(System::IAsyncResult^ ar)
{
    try
    {
        auto listener = dynamic_cast<System::Net::Sockets::TcpListener ^>(ar->AsyncState);
        auto s = listener->EndAcceptSocket(ar);

        auto func = gcnew AsyncCallback(&onAccept);
        listener->BeginAcceptSocket(func, listener);
        delete func;

        if (s != nullptr)
        {
            Logger::log(LogLevel::Information, "Accepted Client : {0}", s->RemoteEndPoint->ToString());

            NetworkProcessor ^processor = gcnew NetworkProcessor(s);
            HandleGuard handleGuard(processor);

            if (!processor->waitForParamReceived(5000))
                goto _close;
            Logger::log(LogLevel::Debug, "ParamReceived");

            auto request = (String ^)processor->receivedParams["Request"];
            Logger::log(LogLevel::Debug, "request : {0}", request);
            if (request == "DownloadMusic")
            {
                onDownloadMusic(processor);
                return;
            }
            else if (request == "DownloadLyric")
            {
                onDownloadLyric(processor);
                return;
            }
            else if (request == "SearchMusic")
            {
                onSearchMusic(processor);
                return;
            }
            else
            {
                s->Close();
                return;
            }
        }
_close:
        s->Close(2000);
        return;
    }
    catch (Sockets::SocketException ^e)
    {
        Logger::log(LogLevel::Error, "Socket Error : {0}", e);
    }
    catch (System::Exception ^e)
    {
        Logger::log(LogLevel::Error, "Unexcepted Error : {0}", e);
    }
}